package de.sendorian.app.forumArchive.domain;

import static org.apache.commons.lang.StringUtils.containsIgnoreCase;
import static org.apache.commons.lang.StringUtils.trim;

import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.regex.Pattern;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Transient;

import org.apache.commons.lang.StringUtils;
import org.hibernate.annotations.Cascade;
import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.FetchMode;

import de.sendorian.app.forumArchive.ApplicationContextHolder;
import de.sendorian.app.forumArchive.Config;

@Entity
public class Topic implements Cloneable {

    public static enum Type {
        ANNOUNCEMENT("Announcement"), STICKY_TOPIC("Sticky Topic"), HOT_TOPIC("Hot Topic"), CLOSED_TOPIC("Closed Topic"), TOPIC(
                "Topic");

        private String detail;


        Type(String detail) {
            this.detail = detail;
        }


        public String getDetail() {
            return detail;
        }


        public static Type valueOfDetail(String typeString) {
            for (Type type : values()) {
                if (containsIgnoreCase(trim(typeString), type.detail))
                    return type;
            }
            return null;
        }
    }

    @Id
    @GeneratedValue
    private int id;

    @Column(length = 10000)
    private String url = "";

    @Column(length = 1000)
    private String name;

    @Enumerated(EnumType.STRING)
    private Type type;

    private Date lastPost;

    @ManyToOne
    private User topicStarter;

    @OneToMany(cascade = CascadeType.ALL, orphanRemoval=true)
    @Fetch(FetchMode.SUBSELECT)
    private List<Post> posts = new ArrayList<Post>();

    @Transient
    Config config;

    // e.g. "file://..." or "http://..."
    private static final Pattern uriPattern = Pattern.compile(".*://.*");


    public int getId() {
        return id;
    }


    public String getUrl() {
        // TODO for backwords compatibility with database < V1.0beta3 only!!
        if (!uriPattern.matcher(url).matches()) {
            url = getConfig().getBaseUrl() + url;
        }
        return url;
    }


    public void setUrl(String url) {
        if (!uriPattern.matcher(url).matches()) {
            url = getConfig().getBaseUrl() + url;
        }
        this.url = url;
    }


    public String getName() {
        return name;
    }


    public void setName(String name) {
        name = name.replace("/", ",").replace("\\", ",");
        this.name = name;
    }


    public Type getType() {
        return type;
    }


    public void setType(Type type) {
        this.type = type;
    }


    public Date getLastPost() {
        return lastPost;
    }


    public void setLastPost(Date lastPost) {
        this.lastPost = lastPost;
    }


    public User getTopicStarter() {
        return topicStarter;
    }


    public void setTopicStarter(User topicStarter) {
        this.topicStarter = topicStarter;
    }


    public List<Post> getPosts() {
        return posts;
    }


    public void setPosts(List<Post> posts) {
        this.posts = posts;
    }


    private Config getConfig() {
        if (this.config == null) {
            this.config = (Config) ApplicationContextHolder.getBean("config");
        }
        return this.config;
    }


    @Override
    public String toString() {
        String lastPost = this.lastPost != null ? " last changed on "
                + DateFormat.getDateTimeInstance().format(this.lastPost) : "";
        return "Topic '" + name + "'" + lastPost + " started by " + topicStarter + " (" + url + ") ";
    }


    public String toStringFormatted() {
        StringBuilder builder = new StringBuilder();
        builder.append(toString() + "\n");
        builder.append("With " + posts.size() + " posts.\n");
        builder.append(LINE);
        for (Post post : posts) {
            builder.append(post.toStringFormatted());
            builder.append('\n');
        }
        return builder.toString();
    }


    @Override
    public Topic clone() {
        try {
            return (Topic) super.clone();
        } catch (CloneNotSupportedException e) {
            return null;
        }
    }


    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        result = prime * result + ((topicStarter == null) ? 0 : topicStarter.hashCode());
        return result;
    }


    /**
     * A topic is equal if the name and the topic starter is equal.
     */
    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Topic other = (Topic) obj;
        if (name == null) {
            if (other.name != null)
                return false;
        } else if (!name.equals(other.name))
            return false;
        if (topicStarter == null) {
            if (other.topicStarter != null)
                return false;
        } else if (!topicStarter.equals(other.topicStarter))
            return false;
        return true;
    }

    private final static String LINE = StringUtils.repeat("*", 100) + "\n";
}
